-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[clang] Add support for __declspec(no_init_all)
#116847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@llvm/pr-subscribers-clang Author: Daniel Paoliello (dpaoliello) ChangesIn MSVC, when This change does the same for Clang, except that it applies to the NOTE:
Full diff: https://github.com/llvm/llvm-project/pull/116847.diff 3 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 6035a563d5fce7..5d3270280c17f6 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4888,3 +4888,10 @@ def ClspvLibclcBuiltin: InheritableAttr {
let Documentation = [ClspvLibclcBuiltinDoc];
let SimpleHandler = 1;
}
+
+def NoTrivialAutoVarInit: InheritableAttr {
+ let Spellings = [Declspec<"no_init_all">];
+ let Subjects = SubjectList<[Function, Tag]>;
+ let Documentation = [Undocumented];
+ let SimpleHandler = 1;
+}
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 6e9d28cea28e79..ff53ffae4459af 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1900,10 +1900,16 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
locIsByrefHeader ? emission.getObjectAddress(*this) : emission.Addr;
// Note: constexpr already initializes everything correctly.
+ auto typeHasNoTrivialAutoVarInitAttr = [&]() {
+ auto *TD = type->getAsTagDecl();
+ return TD && TD->hasAttr<NoTrivialAutoVarInitAttr>();
+ };
LangOptions::TrivialAutoVarInitKind trivialAutoVarInit =
(D.isConstexpr()
? LangOptions::TrivialAutoVarInitKind::Uninitialized
- : (D.getAttr<UninitializedAttr>()
+ : ((D.getAttr<UninitializedAttr>() ||
+ typeHasNoTrivialAutoVarInitAttr() ||
+ CurFuncDecl->hasAttr<NoTrivialAutoVarInitAttr>())
? LangOptions::TrivialAutoVarInitKind::Uninitialized
: getContext().getLangOpts().getTrivialAutoVarInit()));
@@ -1944,13 +1950,13 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
replaceUndef(CGM, isPattern, constant));
}
- if (constant && D.getType()->isBitIntType() &&
- CGM.getTypes().typeRequiresSplitIntoByteArray(D.getType())) {
+ if (constant && type->isBitIntType() &&
+ CGM.getTypes().typeRequiresSplitIntoByteArray(type)) {
// Constants for long _BitInt types are split into individual bytes.
// Try to fold these back into an integer constant so it can be stored
// properly.
- llvm::Type *LoadType = CGM.getTypes().convertTypeForLoadStore(
- D.getType(), constant->getType());
+ llvm::Type *LoadType =
+ CGM.getTypes().convertTypeForLoadStore(type, constant->getType());
constant = llvm::ConstantFoldLoadFromConst(
constant, LoadType, llvm::APInt::getZero(32), CGM.getDataLayout());
}
@@ -1967,8 +1973,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
// It may be that the Init expression uses other uninitialized memory,
// but auto-var-init here would not help, as auto-init would get
// overwritten by Init.
- if (!D.getType()->isScalarType() || capturedByInit ||
- isAccessedBy(D, Init)) {
+ if (!type->isScalarType() || capturedByInit || isAccessedBy(D, Init)) {
initializeWhatIsTechnicallyUninitialized(Loc);
}
}
diff --git a/clang/test/CodeGenCXX/auto-var-init-attr.cpp b/clang/test/CodeGenCXX/auto-var-init-attr.cpp
new file mode 100644
index 00000000000000..5481c6e8613c56
--- /dev/null
+++ b/clang/test/CodeGenCXX/auto-var-init-attr.cpp
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -fblocks -fdeclspec -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s
+
+struct S { char c; };
+class C { char c; };
+enum class E { ZERO };
+union U { char c; int i; };
+
+struct __declspec(no_init_all) NoInitS { char c; };
+class __declspec(no_init_all) NoInitC { char c; };
+enum class __declspec(no_init_all) NoInitE { ZERO };
+union __declspec(no_init_all) NoInitU { char c; int i; };
+
+extern "C" {
+ void test_no_attr() {
+ // CHECK-LABEL: @test_no_attr()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %s = alloca %struct.S, align 1
+ // CHECK-NEXT: %c = alloca %class.C, align 1
+ // CHECK-NEXT: %e = alloca i32, align 4
+ // CHECK-NEXT: %u = alloca %union.U, align 4
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 %s, i8 0, i64 1, i1 false)
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 %c, i8 0, i64 1, i1 false)
+ // CHECK-NEXT: store i32 0, ptr %e, align 4
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %u, i8 0, i64 4, i1 false)
+ // CHECK-NEXT ret void
+ S s;
+ C c;
+ E e;
+ U u;
+ }
+
+ void __declspec(no_init_all) test_attr_on_function() {
+ // CHECK-LABEL: @test_attr_on_function()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %s = alloca %struct.S, align 1
+ // CHECK-NEXT: %c = alloca %class.C, align 1
+ // CHECK-NEXT: %e = alloca i32, align 4
+ // CHECK-NEXT: %u = alloca %union.U, align 4
+ // CHECK-NEXT: ret void
+ S s;
+ C c;
+ E e;
+ U u;
+ }
+
+ void test_attr_on_decl() {
+ // CHECK-LABEL: @test_attr_on_decl()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %s = alloca %struct.NoInitS, align 1
+ // CHECK-NEXT: %c = alloca %class.NoInitC, align 1
+ // CHECK-NEXT: %e = alloca i32, align 4
+ // CHECK-NEXT: %u = alloca %union.NoInitU, align 4
+ // CHECK-NEXT: ret void
+ NoInitS s;
+ NoInitC c;
+ NoInitE e;
+ NoInitU u;
+ }
+}
\ No newline at end of file
|
|
@llvm/pr-subscribers-clang-codegen Author: Daniel Paoliello (dpaoliello) ChangesIn MSVC, when This change does the same for Clang, except that it applies to the NOTE:
Full diff: https://github.com/llvm/llvm-project/pull/116847.diff 3 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 6035a563d5fce7..5d3270280c17f6 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4888,3 +4888,10 @@ def ClspvLibclcBuiltin: InheritableAttr {
let Documentation = [ClspvLibclcBuiltinDoc];
let SimpleHandler = 1;
}
+
+def NoTrivialAutoVarInit: InheritableAttr {
+ let Spellings = [Declspec<"no_init_all">];
+ let Subjects = SubjectList<[Function, Tag]>;
+ let Documentation = [Undocumented];
+ let SimpleHandler = 1;
+}
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 6e9d28cea28e79..ff53ffae4459af 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1900,10 +1900,16 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
locIsByrefHeader ? emission.getObjectAddress(*this) : emission.Addr;
// Note: constexpr already initializes everything correctly.
+ auto typeHasNoTrivialAutoVarInitAttr = [&]() {
+ auto *TD = type->getAsTagDecl();
+ return TD && TD->hasAttr<NoTrivialAutoVarInitAttr>();
+ };
LangOptions::TrivialAutoVarInitKind trivialAutoVarInit =
(D.isConstexpr()
? LangOptions::TrivialAutoVarInitKind::Uninitialized
- : (D.getAttr<UninitializedAttr>()
+ : ((D.getAttr<UninitializedAttr>() ||
+ typeHasNoTrivialAutoVarInitAttr() ||
+ CurFuncDecl->hasAttr<NoTrivialAutoVarInitAttr>())
? LangOptions::TrivialAutoVarInitKind::Uninitialized
: getContext().getLangOpts().getTrivialAutoVarInit()));
@@ -1944,13 +1950,13 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
replaceUndef(CGM, isPattern, constant));
}
- if (constant && D.getType()->isBitIntType() &&
- CGM.getTypes().typeRequiresSplitIntoByteArray(D.getType())) {
+ if (constant && type->isBitIntType() &&
+ CGM.getTypes().typeRequiresSplitIntoByteArray(type)) {
// Constants for long _BitInt types are split into individual bytes.
// Try to fold these back into an integer constant so it can be stored
// properly.
- llvm::Type *LoadType = CGM.getTypes().convertTypeForLoadStore(
- D.getType(), constant->getType());
+ llvm::Type *LoadType =
+ CGM.getTypes().convertTypeForLoadStore(type, constant->getType());
constant = llvm::ConstantFoldLoadFromConst(
constant, LoadType, llvm::APInt::getZero(32), CGM.getDataLayout());
}
@@ -1967,8 +1973,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
// It may be that the Init expression uses other uninitialized memory,
// but auto-var-init here would not help, as auto-init would get
// overwritten by Init.
- if (!D.getType()->isScalarType() || capturedByInit ||
- isAccessedBy(D, Init)) {
+ if (!type->isScalarType() || capturedByInit || isAccessedBy(D, Init)) {
initializeWhatIsTechnicallyUninitialized(Loc);
}
}
diff --git a/clang/test/CodeGenCXX/auto-var-init-attr.cpp b/clang/test/CodeGenCXX/auto-var-init-attr.cpp
new file mode 100644
index 00000000000000..5481c6e8613c56
--- /dev/null
+++ b/clang/test/CodeGenCXX/auto-var-init-attr.cpp
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -fblocks -fdeclspec -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s
+
+struct S { char c; };
+class C { char c; };
+enum class E { ZERO };
+union U { char c; int i; };
+
+struct __declspec(no_init_all) NoInitS { char c; };
+class __declspec(no_init_all) NoInitC { char c; };
+enum class __declspec(no_init_all) NoInitE { ZERO };
+union __declspec(no_init_all) NoInitU { char c; int i; };
+
+extern "C" {
+ void test_no_attr() {
+ // CHECK-LABEL: @test_no_attr()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %s = alloca %struct.S, align 1
+ // CHECK-NEXT: %c = alloca %class.C, align 1
+ // CHECK-NEXT: %e = alloca i32, align 4
+ // CHECK-NEXT: %u = alloca %union.U, align 4
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 %s, i8 0, i64 1, i1 false)
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 %c, i8 0, i64 1, i1 false)
+ // CHECK-NEXT: store i32 0, ptr %e, align 4
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %u, i8 0, i64 4, i1 false)
+ // CHECK-NEXT ret void
+ S s;
+ C c;
+ E e;
+ U u;
+ }
+
+ void __declspec(no_init_all) test_attr_on_function() {
+ // CHECK-LABEL: @test_attr_on_function()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %s = alloca %struct.S, align 1
+ // CHECK-NEXT: %c = alloca %class.C, align 1
+ // CHECK-NEXT: %e = alloca i32, align 4
+ // CHECK-NEXT: %u = alloca %union.U, align 4
+ // CHECK-NEXT: ret void
+ S s;
+ C c;
+ E e;
+ U u;
+ }
+
+ void test_attr_on_decl() {
+ // CHECK-LABEL: @test_attr_on_decl()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %s = alloca %struct.NoInitS, align 1
+ // CHECK-NEXT: %c = alloca %class.NoInitC, align 1
+ // CHECK-NEXT: %e = alloca i32, align 4
+ // CHECK-NEXT: %u = alloca %union.NoInitU, align 4
+ // CHECK-NEXT: ret void
+ NoInitS s;
+ NoInitC c;
+ NoInitE e;
+ NoInitU u;
+ }
+}
\ No newline at end of file
|
|
If we're going to treat |
I think allowing
This is why I wanted to avoid adding a Clang spelling: it feels like it would need a bunch of naming discussions and design work. |
|
I see. I agree we don't need to add non-__declspec syntax here. We're adding this for MSVC compat, so we need to match MSVC semantics anyway, so there's not really any other way to implement this. We can always adjust the code later if we end up implementing an overlapping feature for non-MSVC targets. I'd still like to have a basic description in documentation for reference, especially since there isn't any corresponding MSVC compiler documentation. |
bacaeb4 to
d97c75c
Compare
Done |
| a marked type. Note that this attribute has no effect for locals that are automatically initialized | ||
| without the `-ftrivial-auto-var-init`_ flag. | ||
|
|
||
| .. _`-ftrivial-auto-var-init`: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-ftrivial-auto-var-init |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you write something like the following?
:ref:`option:: -ftrivial-auto-var-init=<arg>`
Not a big deal, but ideally we want people to be able to publish documentation to places other than clang.llvm.org.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made the link relative, but I have no idea how to get the :ref: syntax to work.
In theory it's a link to an option: https://www.sphinx-doc.org/en/master/usage/referencing.html#role-option, but it's also cross-doc, so :option:`-ftrivial-auto-var-init <ClangCommandLineReference:-ftrivial-auto-var-init=<arg>>` should work, but it didn't seem to.
d97c75c to
d14059d
Compare
efriedma-quic
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
efriedma-quic
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Addendum to commit 73b54ce. Conditionally define it on the `_MSC_VER >= 1915, and add the other `MIDL_PASS` and co. guard checks as in the official Windows PSDK. Additionally, no-op the define for Clang to silence the following warnings: ``` sdk\include\ddk\ntddk.h(2050,35): warning: __declspec attribute 'no_init_all' is not supported [-Wignored-attributes] sdk\include\psdk\ntdef.h(40,95): note: expanded from macro 'DECLSPEC_NOINITALL' ``` Granted, this may be due to the fact our GitHub actions currently use Clang 13.0.1: ``` -- The C compiler identification is Clang 13.0.1 with MSVC-like command-line -- The CXX compiler identification is Clang 13.0.1 with MSVC-like command-line ``` while support for `no_init_all` may have been added for Clang 22.0.0, if https://clang.llvm.org/docs/AttributeReference.html#no-init-all is correct. (See PR llvm/llvm-project#116847 )
Addendum to commit 73b54ce. Conditionally define it on the `_MSC_VER >= 1915, and add the other `MIDL_PASS` and co. guard checks as in the official Windows PSDK. Additionally, no-op the define for Clang to silence the following warnings: ``` sdk\include\ddk\ntddk.h(2050,35): warning: __declspec attribute 'no_init_all' is not supported [-Wignored-attributes] sdk\include\psdk\ntdef.h(40,95): note: expanded from macro 'DECLSPEC_NOINITALL' ``` Granted, this may be due to the fact our GitHub actions currently use Clang 13.0.1: ``` -- The C compiler identification is Clang 13.0.1 with MSVC-like command-line -- The CXX compiler identification is Clang 13.0.1 with MSVC-like command-line ``` while support for `no_init_all` may have been added for Clang 22.0.0, if https://clang.llvm.org/docs/AttributeReference.html#no-init-all is correct. (See PR llvm/llvm-project#116847 )
Addendum to commit 73b54ce. Conditionally define it on the `_MSC_VER >= 1915, and add the other `MIDL_PASS` and co. guard checks as in the official Windows PSDK. Additionally, no-op the define for Clang to silence the following warnings: ``` sdk\include\ddk\ntddk.h(2050,35): warning: __declspec attribute 'no_init_all' is not supported [-Wignored-attributes] sdk\include\psdk\ntdef.h(40,95): note: expanded from macro 'DECLSPEC_NOINITALL' ``` Granted, this may be due to the fact our GitHub actions currently use Clang 13.0.1: ``` -- The C compiler identification is Clang 13.0.1 with MSVC-like command-line -- The CXX compiler identification is Clang 13.0.1 with MSVC-like command-line ``` while support for `no_init_all` may have been added for Clang 22.0.0, if https://clang.llvm.org/docs/AttributeReference.html#no-init-all is correct. (See PR llvm/llvm-project#116847 )
In MSVC, when
/d1initallis enabled,__declspec(no_init_all)can be applied to a type to suppress auto-initialization for all instances of that type or to a function to suppress auto-initialization for all locals within that function.This change does the same for Clang, except that it applies to the
-ftrivial-auto-var-initflag instead.NOTE: I did not add a Clang-specific spelling for this but would be happy to make a followup PR if folks are interested in that.